//=============================================================================
/**
 *
 *	@file		catch_ai.c
 *	@brief		UFOキャッチ AI
 *	@author		hosaka genya
 *	@data		2008.12.01
 *
 */
//=============================================================================
#include "common.h"

#include "../../pkthln_comm_pack.h"
#include "../../pkthln_obj_common.h"
#include "../../pokethlon.h"

#include "pkthln_catch.h"
#include "catch_define.h"
#include "catch_ai.h"

typedef struct POKE_AI_tag * POKE_AI_PTR;
typedef struct CATCH_AI_tag * CATCH_AI_PTR;

//=============================================================================
/**
 *								定数定義
 */
//=============================================================================
enum
{
	AI_MAX = ENTRY_MEMBER_NUM_MAX - 1,	///< AIで管理する最大数
};
//--------------------------------------------------------------
///
//==============================================================
enum
{
	AI_SEQ_SEARCH = 0,	///< UFO探索
	AI_SEQ_HUNT,		///< UFO追尾
};

//=============================================================================
/**
 *								構造体定義
 */
//=============================================================================

//--------------------------------------------------------------
///
//==============================================================
typedef struct POKE_AI_tag {
	// [IN]
	PKTHLN_PTR work;
	// [PRIVATE]
	POS_DATA Pos;
	s16 TarPosX;
	u32 bLockon : 1;
	u32 LockUfoIdx : 2;
	u32 NetID : 2;
	u32 PokeID : 2;
	u32 ActSeq : 4;
	u32 ActionTimer : 8;
	u32 DummyBit : 13;
	u8 PosYCounter;
} POKE_AI;


//--------------------------------------------------------------
///
//==============================================================
typedef struct CATCH_AI_tag {
	// [PRIVATE]
	POKE_AI Poke[ ENTRY_MAX ];
} CATCH_AI;

//--------------------------------------------------------------
///	AI Controller
//==============================================================
typedef struct CATCH_AI_CNT_tag {
	// [IN]
	HEAPID HeapID;
	PKTHLN_PTR work;
	// [PRIVATE]
	CATCH_AI AI[ AI_MAX ];
	u8 AI_Count;	///< AIの数
} CATCH_AI_CNT;

//=============================================================================
/**
 *							プロトタイプ宣言
 */
//=============================================================================
// -- prototype --
static void AI_Setup( CATCH_AI_PTR ptr, u8 NetID, PKTHLN_PTR work );
static void AI_Proc( CATCH_AI_PTR ptr, u16 Average, u16 MyScore );
static void PokeAI_Setup( POKE_AI_PTR ptr, PKTHLN_PTR work, u8 ID );
static void PokeAI_Action( POKE_AI_PTR ptr );
static void PokeAI_Proc( POKE_AI_PTR ptr, u16 Average, u16 MyScore );
static BOOL PokeAI_CheckHit( POKE_AI_PTR ptr, int UfoIdx, int ufo_range );
static BOOL Circle_HitCheck( s16 px1, s16 py1, int r1, s16 px2, s16 py2, int r2 );

//=============================================================================
/**
 *								外部公開関数
 */
//=============================================================================

//-----------------------------------------------------------------------------
/**
 *	@brief	AIコントローラー作成
 *
 *	@param	PKTHLN_PTR work
 *	@param	HeapID
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
CATCH_AI_CNT_PTR CatchAICnt_Create( PKTHLN_PTR work, HEAPID HeapID )
{
	int i;
	u8 PlayerNum;
	CATCH_AI_CNT_PTR cnt;

	PlayerNum = PTFrame_GetPlayerNum(work);

	cnt = sys_AllocMemory( HeapID, sizeof(CATCH_AI_CNT) );
	MI_CpuClear8( cnt, sizeof(CATCH_AI_CNT) );

	cnt->HeapID = HeapID;
	cnt->work  = work;
	cnt->AI_Count = ENTRY_MEMBER_NUM_MAX - PlayerNum;

	for( i=0; i<cnt->AI_Count; i++ )
	{
		AI_Setup( &cnt->AI[i], PlayerNum + i, work );
	}

	HOSAKA_PRINT("AI_Count:%d \n", cnt->AI_Count);

	return cnt;
}

//-----------------------------------------------------------------------------
/**
 *	@brief	AIコントローラー削除
 *
 *	@param	CATCH_AI_CNT_PTR cnt
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
void CatchAICnt_Delete( CATCH_AI_CNT_PTR cnt )
{
	sys_FreeMemoryEz( cnt );
}

//-----------------------------------------------------------------------------
/**
 *	@brief	AI処理
 *
 *	@param	CATCH_AI_CNT_PTR cnt
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
void CatchAICnt_Proc( CATCH_AI_CNT_PTR cnt )
{
	PKTHLN_CATCH_PTR cp = PTFrame_GetEventWorkPtr(cnt->work);
	int Average = 0;
	int i;

	// 平均点を算出
	for( i=0; i<ENTRY_MEMBER_NUM_MAX; i++ )
	{
		Average += Catch_GetScore( cp, i );
	}
	Average /= ENTRY_MEMBER_NUM_MAX;

	for( i=0; i<cnt->AI_Count; i++ )
	{
		int MyScore = Catch_GetScore( cp, i );
		AI_Proc( &cnt->AI[i], Average, MyScore );
	}

}

//=============================================================================
/**
 *								static関数
 */
//=============================================================================

//-----------------------------------------------------------------------------
/**
 *	@brief	チームAI 初期化
 *
 *	@param	CATCH_AI_PTR ptr
 *	@param	NetID
 *	@param	work
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void AI_Setup( CATCH_AI_PTR ptr, u8 NetID, PKTHLN_PTR work )
{
	int i;

	MI_CpuClear8( ptr, sizeof(CATCH_AI) );

	for( i=0; i< ENTRY_MAX; i++ )
	{
		PokeAI_Setup( &ptr->Poke[i], work, NetID * ENTRY_MAX + i );
	}
}

//-----------------------------------------------------------------------------
/**
 *	@brief	チームAI 主処理
 *
 *	@param	CATCH_AI_PTR ptr
 *	@param	Average
 *	@param	MyScore
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void AI_Proc( CATCH_AI_PTR ptr, u16 Average, u16 MyScore )
{
	int i;

	for( i=0; i<ENTRY_MAX; i++ )
	{
		PokeAI_Proc( &ptr->Poke[i], Average, MyScore );
	}
}

//-----------------------------------------------------------------------------
/**
 *	@brief	Y軸をブラす
 *
 *	@param	POKE_AI_PTR ptr
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void PokeAI_SetPosRand( POKE_AI_PTR ptr )
{
	static const u8 c_PosYParam[3][2] = {
		{ 3, 5 },
		{ 7, 6 },
		{ 15, 6 },
	};

	ptr->Pos.Y = c_PosYParam[ ptr->PokeID ][0] + ( gf_mtRand() % c_PosYParam[ ptr->PokeID ][1] );
	ptr->Pos.Y *= 8;
	ptr->TarPosX = g_PokeInitPos[ ptr->NetID ][ ptr->PokeID ].X;
	ptr->TarPosX += ( ( gf_mtRand() % (4+1) ) - 2 ) * 8;
	ptr->bLockon = 0;
	ptr->ActSeq = AI_SEQ_SEARCH;
}

//-----------------------------------------------------------------------------
/**
 *	@brief	ポケモンAI 初期化
 *
 *	@param	POKE_AI_PTR ptr
 *	@param	work
 *	@param	ID
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void PokeAI_Setup( POKE_AI_PTR ptr, PKTHLN_PTR work, u8 ID )
{

	u8 NetID = ID / ENTRY_MAX;
	u8 PokeID =  ID % ENTRY_MAX;

	ptr->work = work;
	ptr->NetID = NetID;
	ptr->PokeID = PokeID;
	ptr->ActionTimer = NetID * 2 + PokeID * 8;	///< バラけさせる
	ptr->PosYCounter = NetID * 2 + PokeID * 8;	///< バラけさせる

	// Y初期化
	PokeAI_SetPosRand( ptr );
}

//-----------------------------------------------------------------------------
/**
 *	@brief	ポケモンAI アクション駆動
 *
 *	@param	POKE_AI_PTR ptr
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void PokeAI_Action( POKE_AI_PTR ptr )
{
	int i;
	PKTHLN_CATCH_PTR cp;
	const UFO_DATA* pUfo;
	u8 Rank;

	cp = PTFrame_GetEventWorkPtr(ptr->work);
	Rank = Catch_GetNowRank( ptr->work, ptr->NetID );

	switch( ptr->ActSeq )
	{
	case AI_SEQ_SEARCH :
		GF_ASSERT( ptr->bLockon == 0 );

		// 所定地にもどる
		ptr->Pos.X = ptr->TarPosX;

		// UFO探索
		for( i=0; i<UFO_ID_MAX; i++ )
		{
			pUfo = Catch_GetUfoData( cp, i );

			if( pUfo->bAlive && PokeAI_CheckHit( ptr, i, AI_UFO_RANGE_SEARCH ) )
			{
				// UFO発見
				ptr->LockUfoIdx = i;
				ptr->bLockon = 1;
				ptr->ActSeq = AI_SEQ_HUNT;
//				HOSAKA_PRINT("[%d,%d]AI Lock Ufo[%d] !\n",ptr->NetID,ptr->PokeID,i);
			}
		}
		break;

	case AI_SEQ_HUNT :
		GF_ASSERT( ptr->bLockon == 1 );

		// UFO追尾
		if( ptr->bLockon )
		{
			pUfo = Catch_GetUfoData( cp, ptr->LockUfoIdx );

			if( pUfo->bAlive )
			{
				// UFOの方向からX座標を設定
				ptr->Pos.X = Catch_GetUfoPosX( ptr->Pos.Y + ( GX_LCD_SIZE_Y + DISP_DISTANCE ), pUfo->Vec );
			}
			else
			{
				// UFO失踪
				ptr->bLockon = 0;
				ptr->ActSeq = AI_SEQ_SEARCH;
			}
		}
		break;

	default : GF_ASSERT(0);
	}

	Catch_SetPokeMovePos( cp, &ptr->Pos, ptr->NetID, ptr->PokeID );
}

//-----------------------------------------------------------------------------
/**
 *	@brief	ポケモンAI 主処理
 *
 *	@param	POKE_AI_PTR ptr
 *	@param	Average
 *	@param	MyScore
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void PokeAI_Proc( POKE_AI_PTR ptr, u16 Average, u16 MyScore )
{
	PKTHLN_CATCH_PTR cp;

	cp = PTFrame_GetEventWorkPtr(ptr->work);

	// ポケモンが操作不能なら処理なし
	if( Catch_GetPokeActiveFlag( cp, ptr->NetID, ptr->PokeID ) == FALSE )
	{
		return;
	}

	// Y座標を再抽選
	if( --ptr->PosYCounter <= 0 )
	{
		PokeAI_SetPosRand( ptr );
		ptr->PosYCounter = AI_POS_RAND_SPAN;
	}

	// AI駆動
	if( --ptr->ActionTimer <= 0 )
	{
		PokeAI_Action( ptr );
		ptr->ActionTimer = AI_ACTION_SPAN;
	}

	// ジャンプ判定
	if( ptr->bLockon && PokeAI_CheckHit( ptr, ptr->LockUfoIdx, AI_UFO_RANGE_JUMP ) )
	{
		POS_DATA PPos;
		int PokeRange;
		int rand = gf_mtRand() % 100;
		int parsent;
		int diff;

		Catch_GetPokeNowPos( cp, ptr->NetID, ptr->PokeID, &PPos, &PokeRange );

		// 平均点が一定数離れていたらジャンプしない
		if( Average + AI_JUMP_AVERAGE_ADD < MyScore )
		{
			parsent = 0;
		}
		else
		{
			parsent = g_AIJumpParsent[ptr->PokeID];
		}

		if( rand < parsent )
		{
			HOSAKA_PRINT("[%d,%d] Average=%d, Parsent:%d \n",ptr->NetID, ptr->PokeID, Average, parsent);
			Catch_SetPokeJump( cp, ptr->NetID, ptr->PokeID );
		}
	}
}

//-----------------------------------------------------------------------------
/**
 *	@brief	ポケモンAI UFOの当たり判定
 *
 *	@param	POKE_AI_PTR ptr
 *	@param	UfoIdx
 *	@param	ufo_range
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static BOOL PokeAI_CheckHit( POKE_AI_PTR ptr, int UfoIdx, int ufo_range )
{
	PKTHLN_CATCH_PTR cp;
	const UFO_DATA* pUfo;
	POS_DATA PPos;
	POS_DATA UPos;
	int PokeRange;

	cp = PTFrame_GetEventWorkPtr(ptr->work);
	pUfo = Catch_GetUfoData( cp, UfoIdx );

	// 上画面の間は判定しない
	if( pUfo->PosY < GX_LCD_SIZE_Y + DISP_DISTANCE ){ return FALSE; }

	Catch_GetPokeNowPos( cp, ptr->NetID, ptr->PokeID, &PPos, &PokeRange );

	UPos.X = pUfo->PosX;
	UPos.Y = pUfo->PosY - ( GX_LCD_SIZE_Y + DISP_DISTANCE );

	return Circle_HitCheck( PPos.X, PPos.Y, PokeRange, UPos.X, UPos.Y, ufo_range );
}

//-----------------------------------------------------------------------------
/**
 *	@brief	円同士の当たり判定
 *
 *	@param	s16 px1
 *	@param	py1
 *	@param	r1
 *	@param	px2
 *	@param	py2
 *	@param	r2
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static BOOL Circle_HitCheck( s16 px1, s16 py1, int r1, s16 px2, s16 py2, int r2 )
{
	return ( (r1 + r2) * (r1 + r2) >=
			 MATH_IAbs( px1 - px2 ) * MATH_IAbs( px1 - px2 ) +
			 MATH_IAbs( py1 - py2 ) * MATH_IAbs( py1 - py2 ) );
}
